//////////////////////////////////////////////////////
// Direct3D.cpp
//////////////////////////////////////////////////////

#include "direct3d.h"

//-----------------------------------------------------------------------------
// Transformed, lit vertex
//-----------------------------------------------------------------------------
struct D3DTLVERTEX
{
	float    x, y, z; // Position
	float    rhw;     // Reciprocal of homogeneous w
	D3DCOLOR colour;  // Vertex colour
	float    tu, tv;  // Texture coordinates
};
const DWORD D3DFVF_TLVERTEX = D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1;

//////////////////////////////////////////////////////
// CDirect3D()
//////////////////////////////////////////////////////
CDirect3D::CDirect3D(void)
{
    m_pD3D = NULL;
    m_pD3DDevice = NULL;
    m_pBackBuffer = NULL;
    strcpy(m_szErrorMsg, "No Error");
    m_pTrainingGroundsSurfaces = NULL;
    m_pCurrentTileSurface = NULL;
    m_pPlayerTile = NULL;
    m_pTexture = NULL;
    m_pTitleScreenCursor = NULL;
}

//////////////////////////////////////////////////////
// ~CDirect3D()
//////////////////////////////////////////////////////
CDirect3D::~CDirect3D(void)
{
    CleanUpEverythingElse();
}

//////////////////////////////////////////////////////
// SetWindowHandle()
//////////////////////////////////////////////////////
void CDirect3D::SetWindowHandle(HWND hWnd)
{
    m_hWnd = hWnd;
}

//////////////////////////////////////////////////////
// InitD3D()
//////////////////////////////////////////////////////
HRESULT CDirect3D::InitD3D()
{
    HRESULT hResult = CreateD3DObject();
    if (hResult != D3D_OK)
        return hResult;
    hResult = CheckDisplayMode();
    if (hResult != D3D_OK)
        return hResult;
    hResult = CreateD3DDevice();
    if (hResult != D3D_OK)
        return hResult;
    hResult = CreateTitleScreenSurfaces();
    if (hResult != D3D_OK)
        return hResult;
    hResult = m_pD3DDevice->GetBackBuffer(0,
        D3DBACKBUFFER_TYPE_MONO, &m_pBackBuffer);
    m_pD3DDevice->Clear(0, 0, D3DCLEAR_TARGET, 
        D3DCOLOR_XRGB(0,0,255), 0, 0);
    m_pD3DDevice->Present(NULL, NULL, NULL, NULL);
    return hResult;
}

//////////////////////////////////////////////////////
// CreateD3DObject()
//////////////////////////////////////////////////////
HRESULT CDirect3D::CreateD3DObject()
{
    m_pD3D = Direct3DCreate8(D3D_SDK_VERSION);
    if (m_pD3D == NULL)
    {
        MessageBox(m_hWnd, "Couldn't create DirectX object.",
            "DirectX Error", MB_OK);
        strcpy(m_szErrorMsg, "CreateD3DObject()");
        return E_FAIL;
    }
    return D3D_OK;
}

//////////////////////////////////////////////////////
// CheckDisplayMode()
//////////////////////////////////////////////////////
HRESULT CDirect3D::CheckDisplayMode()
{
    HRESULT hResult = m_pD3D->CheckDeviceType(D3DADAPTER_DEFAULT, 
        D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DFMT_X8R8G8B8, FALSE);
    if (hResult != D3D_OK)
    {
        MessageBox(m_hWnd, "The required display mode is \
not available on this system.",
            "DirectX Error", MB_OK);
        strcpy(m_szErrorMsg, "CheckDisplayMode()");
        return hResult;
    }
    return D3D_OK;
}

//////////////////////////////////////////////////////
// CreateD3DDevice()
//////////////////////////////////////////////////////
HRESULT CDirect3D::CreateD3DDevice()
{
    D3DPRESENT_PARAMETERS D3DPresentParams;
    ZeroMemory(&D3DPresentParams, sizeof(D3DPRESENT_PARAMETERS));
    D3DPresentParams.Windowed = FALSE;
    D3DPresentParams.BackBufferCount = 1;
    D3DPresentParams.BackBufferWidth = 800;
    D3DPresentParams.BackBufferHeight = 600;
    D3DPresentParams.BackBufferFormat = D3DFMT_X8R8G8B8;
    D3DPresentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
    D3DPresentParams.hDeviceWindow = m_hWnd;
    HRESULT hResult = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, 
        D3DDEVTYPE_HAL, m_hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
        &D3DPresentParams, &m_pD3DDevice);

	if (FAILED(hResult))
    {
        MessageBox(m_hWnd, "Failed to create Direct3D device.",
            "DirectX Error", MB_OK);
        strcpy(m_szErrorMsg, "CreateD3DDevice()");
        return hResult;
    }
    return hResult;
}
//////////////////////////////////////////////////////
// CreateNonMapSurfaces()
//////////////////////////////////////////////////////
HRESULT CDirect3D::CreateNonMapSurfaces()
{

	HRESULT hResult = D3DXCreateTextureFromFileEx(m_pD3DDevice, "Graphics\\Player.bmp",
        D3DX_DEFAULT, D3DX_DEFAULT, 1, 0, D3DFMT_A8R8G8B8,
        D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(255, 0, 255),
        NULL, NULL, &m_pPlayerTile);
    if (FAILED(hResult))
    {
        strcpy(m_szErrorMsg, "Error loading Player texture surface.");
        return hResult;
    }

	
    hResult = D3DXCreateTextureFromFileEx(m_pD3DDevice, "Graphics\\Plaque.png",
        D3DX_DEFAULT, D3DX_DEFAULT, 1, 0, D3DFMT_A8R8G8B8,
        D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(0, 0, 0),
        NULL, NULL, &m_pTexture);
    if (FAILED(hResult))
    {
        strcpy(m_szErrorMsg, "Error loading texture surface.");
		PostQuitMessage(WM_QUIT);
        return hResult;
    }
	
	
    return hResult;
}

//////////////////////////////////////////////////////
// CreateTitleScreenSurfaces()
//////////////////////////////////////////////////////
HRESULT CDirect3D::CreateTitleScreenSurfaces()
{
	//Added Create surface function for Title screen 11-30-06
	HRESULT hResult = m_pD3DDevice->CreateImageSurface(800, 600, 
        D3DFMT_X8R8G8B8, &m_pTitleScreen);
    if (FAILED(hResult))
    {
        strcpy(m_szErrorMsg, "Title screen surface not created.");
        return hResult;
    }

    hResult = D3DXLoadSurfaceFromFile(m_pTitleScreen, NULL, NULL,
        "Graphics\\TitleScreen.png", NULL, D3DX_DEFAULT, 0, NULL);
    if (FAILED(hResult))
    {
        strcpy(m_szErrorMsg, "Error loading Title screen surface.");
        return hResult;
    }

	hResult = m_pD3DDevice->CreateImageSurface(800, 600, 
        D3DFMT_X8R8G8B8, &m_pTitleScreenOptions);
    if (FAILED(hResult))
    {
        strcpy(m_szErrorMsg, "Title screen Options surface not created.");
        return hResult;
    }

    hResult = D3DXLoadSurfaceFromFile(m_pTitleScreenOptions, NULL, NULL,
        "Graphics\\TitleScreenOptions.png", NULL, D3DX_DEFAULT, 0, NULL);
    if (FAILED(hResult))
    {
        strcpy(m_szErrorMsg, "Error loading Title screen Options  surface.");
        return hResult;
    }

	hResult = m_pD3DDevice->CreateImageSurface(800, 600, 
        D3DFMT_X8R8G8B8, &m_pTitleScreenQuit);
    if (FAILED(hResult))
    {
        strcpy(m_szErrorMsg, "Title screen Quit surface not created.");
        return hResult;
    }

    hResult = D3DXLoadSurfaceFromFile(m_pTitleScreenQuit, NULL, NULL,
        "Graphics\\TitleScreenQuit.png", NULL, D3DX_DEFAULT, 0, NULL);
    if (FAILED(hResult))
    {
        strcpy(m_szErrorMsg, "Error loading Title screen Quit surface.");
        return hResult;
    }

	//Added Create surface function for Title screen 11-30-06

	hResult = D3DXCreateTextureFromFileEx(m_pD3DDevice, "Graphics\\TitleScreenCursor.bmp",
		D3DX_DEFAULT, D3DX_DEFAULT, 1, 0, D3DFMT_A8R8G8B8,
		D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(255, 0, 255),
		NULL, NULL, &m_pTitleScreenCursor);
	if (FAILED(hResult))
    {
        strcpy(m_szErrorMsg, "Error loading Title screen Cursor Texture.");
        return hResult;
    }
	
	
	return hResult;
}
//////////////////////////////////////////////////////
// CreateTrainingGroundsSurfaces()
//////////////////////////////////////////////////////
HRESULT CDirect3D::CreateTrainingGroundsSurfaces()
{
	//Added Create surface function for Training grounds 12-09-06, edited 1-1-07

	//can we delete this now?? should be able to
	HRESULT hResult = m_pD3DDevice->CreateImageSurface(1000, 1000, 
        D3DFMT_X8R8G8B8, &m_pTrainingGroundsSurfaces);
    if (FAILED(hResult))
    {
        strcpy(m_szErrorMsg, "Training Grounds surface not created.");
        return hResult;
    }

    hResult = D3DXLoadSurfaceFromFile(m_pTrainingGroundsSurfaces, NULL, NULL,
        "Graphics\\TrainingGrounds.png", NULL, D3DX_DEFAULT, 0, NULL);
    if (FAILED(hResult))
    {
        strcpy(m_szErrorMsg, "Error loading Training Grounds surface.");
        return hResult;
    }
	

	for (int index = 0; index < NUM_SPRITES; index++)
			{
				g_lpTextures[index] = NULL;
			}

	// Initialise the textures.
	
	RECT srcRect = { 0, 0, TILESIZE - 1, TILESIZE - 1 };

	CreateSurfaceFromFile(&lpSurface, "Graphics\\TrainingGrounds.png");

	for (int index = 0; index < NUM_SPRITES; index++)
	{
		CreateTextureFromSurface(lpSurface, &srcRect, &g_lpTextures[index]);
		if((index+1)%10 == 0 )
		{
			srcRect.left  = 0;
			srcRect.right = TILESIZE - 1;
			srcRect.top    += TILESIZE;
			srcRect.bottom += TILESIZE; 
		}
		else
		{
			srcRect.left  += TILESIZE;
			srcRect.right += TILESIZE;
		}
	}
	if(lpSurface)
		lpSurface->Release();

	return hResult;
}
//-----------------------------------------------------------------------------
// Sets up Direct3D for blitting.
//-----------------------------------------------------------------------------
void CDirect3D::InitBlit()
{
	// Turn off culling, so we see the front and back of primitives
	HRESULT hResult = m_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

	// Enable alpha blended transparency.
	hResult = m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	hResult = m_pD3DDevice->SetRenderState(D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA);
	hResult = m_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

	// Allow modulation of the texture's and diffuse colour's alpha.
	// By default, the texture and diffuse colour's RGB are modulated.
	// This lets us create transparency and tinting effects by setting 
	// the (diffuse) colour in Blit().
	hResult = m_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
	hResult = m_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
	hResult = m_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);

	hResult = m_pD3DDevice->SetVertexShader(D3DFVF_TLVERTEX);
}
//-----------------------------------------------------------------------------
// Draws a textured quad (sprite).
//
// pTexture - The source texture used for the sprite.
// pDest    - Draw sprite at these screen coordinates.
// pCenter  - Centre of scaling and rotation, relative to pDest.
// pScaling - Scaling vector. If NULL, it is treated as identity.
// angle    - Angle of rotation in radians.
// colour   - The RGB and alpha channels are modulated by this value. Use 
//            0xFFFFFFFF for a standard blit.
//-----------------------------------------------------------------------------
void CDirect3D::Blit(IDirect3DTexture8* pTexture, POINT* pDest, D3DXVECTOR2* pCentre, 
          D3DXVECTOR2* pScaling, float angle, D3DCOLOR colour)
{
	D3DSURFACE_DESC surfDesc;
	HRESULT hResult = pTexture->GetLevelDesc(0, &surfDesc);

	float left   = (float)pDest->x - 50;
	float top    = (float)pDest->y - 75;
	float right  = left + surfDesc.Width; // - 1;
	float bottom = top + surfDesc.Height; // - 1;

	const float z = 0.0f, rhw = 1.0f;
	D3DTLVERTEX vertices[4] =
	{
		// x, y, z, rhw, colour, tu, tv
		{ left,  top,    z, rhw, colour, 0, 0 },
		{ right, top,    z, rhw, colour, 1, 0 },
		{ right, bottom, z, rhw, colour, 1, 1 },
		{ left,  bottom, z, rhw, colour, 0, 1 }
	};

	D3DXVECTOR2 centre, scaling;

	centre.x = (float)pDest->x + pCentre->x;
	centre.y = (float)pDest->y + pCentre->y;
	pCentre = &centre; // Don't want to modify the argument passed in.

	if (pScaling == NULL) // Use identity: no scaling performed.
	{
		scaling.x = scaling.y = 1;
		pScaling = &scaling;
	}

	//TransformVertices(vertices, pCentre, pScaling, angle);

	// Draw the sprite
	hResult = m_pD3DDevice->SetTexture(0, pTexture);
	hResult = m_pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, 
	                                       vertices, sizeof(D3DTLVERTEX));
}
//-----------------------------------------------------------------------------
// Transform the points in vertices[] according to the arguments
//
// pCentre  - The centre of scaling and rotation.
// pScaling - Scaling vector.
// angle    - Angle of rotation in radians.
//-----------------------------------------------------------------------------
/*void CDirect3D::TransformVertices(D3DTLVERTEX vertices[], D3DXVECTOR2* pCentre, D3DXVECTOR2* pScaling, float angle)
{
	D3DXMATRIX matTransf, matVerts, matNew;

	BuildMatrix(&matTransf, pCentre, pScaling, angle);

	int cr;
	for (cr = 0; cr < 4; cr++)
	{
		// 4 vertices fit nicely into a 4x4 matrix --
		// Put each vertex point into a matrix row.
		matVerts(cr, 0) = vertices[cr].x;
		matVerts(cr, 1) = vertices[cr].y;
		matVerts(cr, 2) = vertices[cr].z;
		matVerts(cr, 3) = 1.0f;  // the scaling factor, w
	}

	// Reuse D3D matrix multiplication code to transform our 
	// vertices.
	matNew = matVerts * matTransf;

	for (cr = 0; cr < 4; cr++)
	{
		// Retrieve the newly transformed points.
		vertices[cr].x = matNew(cr, 0);
		vertices[cr].y = matNew(cr, 1);
		vertices[cr].z = matNew(cr, 2);
	}
}
*/

//-----------------------------------------------------------------------------
// Creates a surface from the specified graphics file.
//
//  ppSurface  - The created surface object.
//  szFileName - The bitmap file name.
//  D3DCOLOR_XRGB(255, 0, 255)  - The transparent colour.
//-----------------------------------------------------------------------------
void CDirect3D::CreateSurfaceFromFile(LPDIRECT3DSURFACE8* ppSurface, char szFileName[])
{
	const D3DFORMAT SURFACE_FORMAT = D3DFMT_A8R8G8B8;

	LPDIRECT3DSURFACE8 pSurface;
	D3DXIMAGE_INFO srcInfo;    // Optional
	PALETTEENTRY palette[256]; // Optional

	// A quick hack to get the size of the image into srcInfo.
	HRESULT	hResult = m_pD3DDevice->CreateImageSurface(1, 1, 
		SURFACE_FORMAT, &pSurface);
	hResult = D3DXLoadSurfaceFromFile(pSurface, NULL, NULL, szFileName, 
		NULL, D3DX_FILTER_NONE, 0, &srcInfo);
	pSurface->Release();

	// Create a surface to hold the entire file
	hResult = m_pD3DDevice->CreateImageSurface(srcInfo.Width, srcInfo.Height, 
		SURFACE_FORMAT, ppSurface);
	

	pSurface = *ppSurface;

	// The default colour key is D3DCOLOR_XRGB(255, 0, 255) pink :-p
	hResult = D3DXLoadSurfaceFromFile(pSurface, palette, NULL, szFileName, 
		NULL, D3DX_FILTER_NONE, D3DCOLOR_XRGB(255, 0, 255), &srcInfo);

}

//==========================================================================================================

//-----------------------------------------------------------------------------
// Creates a texture from a region of the surface.
//
// pSurface  - the source surface.
// pSrcRect  - portion of the source surface to create the texture from.
// ppTexture - the created texture object.
//-----------------------------------------------------------------------------
HRESULT CDirect3D::CreateTextureFromSurface(IDirect3DSurface8* pSurface, RECT* pSrcRect, LPDIRECT3DTEXTURE8* ppTexture)
{
	int width  = pSrcRect->right - pSrcRect->left + 1;
	int height = pSrcRect->bottom - pSrcRect->top + 1;
	D3DSURFACE_DESC surfDesc;

	pSurface->GetDesc(&surfDesc);
	HRESULT hResult = D3DXCreateTexture(m_pD3DDevice, width, height, 
		1, 0, surfDesc.Format, D3DPOOL_DEFAULT, ppTexture);

	// Retrieve the surface image of the texture.
	LPDIRECT3DSURFACE8 pTexSurface;
	LPDIRECT3DTEXTURE8 pTexture = *ppTexture;
	hResult = pTexture->GetLevelDesc(0, &surfDesc);
	hResult = pTexture->GetSurfaceLevel(0, &pTexSurface);

	// Create a clean surface to clear the texture with.
	LPDIRECT3DSURFACE8 pCleanSurface;
	D3DLOCKED_RECT lockRect;
	hResult = m_pD3DDevice->CreateImageSurface(
		surfDesc.Width, surfDesc.Height, surfDesc.Format, &pCleanSurface);
	hResult = pCleanSurface->LockRect(&lockRect, NULL, 0);
	memset((BYTE*)lockRect.pBits, 0, surfDesc.Height * lockRect.Pitch);
	hResult = pCleanSurface->UnlockRect();

	hResult = m_pD3DDevice->CopyRects(pCleanSurface, NULL, 0, 
		pTexSurface, NULL);
	pCleanSurface->Release();

	// Copy the image to the texture.
	POINT destPoint = { 0, 0 };
	hResult = m_pD3DDevice->CopyRects(pSurface, pSrcRect, 1, 
		pTexSurface, &destPoint);
	pTexSurface->Release();

	return hResult;
}
//////////////////////////////////////////////////////
// CleanUpEverythingElseElse()
//////////////////////////////////////////////////////
void CDirect3D::CleanUpEverythingElse()
{

	// Needed to prevent a memory leak. See documentation for SetTexture().
	m_pD3DDevice->SetTexture(0, NULL);

		if (m_pBackBuffer)
			m_pBackBuffer->Release();

		if (m_pD3DDevice) 
			m_pD3DDevice->Release();

		if (m_pD3D)
			m_pD3D->Release();
		
		if (m_pTexture)
			m_pTexture->Release();
		
		if (m_pPlayerTile) 
			m_pPlayerTile->Release();

}

//////////////////////////////////////////////////////
// CleanUpTitleScreen()
//////////////////////////////////////////////////////
void CDirect3D::CleanUpTitleScreen()
{
	if (m_pTitleScreenCursor)
		m_pTitleScreenCursor->Release();

	if(m_pTitleScreen)
		m_pTitleScreen->Release();

	if(m_pTitleScreenOptions)
		m_pTitleScreenOptions->Release();

	if(m_pTitleScreenQuit)
		m_pTitleScreenQuit->Release();

}

//////////////////////////////////////////////////////
// CleanUpTrainingGrounds()
//////////////////////////////////////////////////////
void CDirect3D::CleanUpTrainingGrounds()
{
    if(m_pTrainingGroundsSurfaces)
		m_pTrainingGroundsSurfaces->Release();		
		

}
//////////////////////////////////////////////////////
// GetBackBuffer()
//////////////////////////////////////////////////////
IDirect3DSurface8* CDirect3D::GetBackBuffer()
{
    return m_pBackBuffer;
}

//////////////////////////////////////////////////////
// GetDevice()
//////////////////////////////////////////////////////
LPDIRECT3DDEVICE8 CDirect3D::GetDevice()
{
    return m_pD3DDevice;
}
//////////////////////////////////////////////////////
// GetErrorString()
//////////////////////////////////////////////////////
char* CDirect3D::GetErrorString(void)
{
    return m_szErrorMsg;
}
//////////////////////////////////////////////////////
// GetCurrentTileSurface()
//////////////////////////////////////////////////////
IDirect3DSurface8* CDirect3D::GetCurrentTileSurface()
{
    return m_pCurrentTileSurface;
}
//////////////////////////////////////////////////////
// SetCurrentTileSurface()
//////////////////////////////////////////////////////
void CDirect3D::SetCurrentTileSurface(IDirect3DSurface8* pSurface)
{
    m_pCurrentTileSurface = pSurface;
}
//////////////////////////////////////////////////////
// GetPlayerTile()
//////////////////////////////////////////////////////
IDirect3DTexture8* CDirect3D::GetPlayerTile()
{
    return m_pPlayerTile;
}
//////////////////////////////////////////////////////
// GetTexture()
//////////////////////////////////////////////////////
IDirect3DTexture8* CDirect3D::GetTexture()
{
    return m_pTexture;
}
//////////////////////////////////////////////////////
// GetTrainingGroundsSurfaces()
//////////////////////////////////////////////////////
IDirect3DSurface8* CDirect3D::GetTrainingGroundsSurfaces()
{
    return m_pTrainingGroundsSurfaces;
}
//////////////////////////////////////////////////////
// GetpTextSurface()
//////////////////////////////////////////////////////
LPDIRECT3DTEXTURE8 CDirect3D::Getg_lpTextures(int whichbitmap)
{
    return g_lpTextures[whichbitmap];
}

//////////////////////////////////////////////////////
// GetTitleScreenCursor()
//////////////////////////////////////////////////////
IDirect3DTexture8* CDirect3D::GetTitleScreenCursor()
{
    return m_pTitleScreenCursor;
}
//////////////////////////////////////////////////////
// GetTitleScreen()
//////////////////////////////////////////////////////
IDirect3DSurface8* CDirect3D::GetTitleScreen()
{
    return m_pTitleScreen;
}
//////////////////////////////////////////////////////
// GetTitleScreenOptions()
//////////////////////////////////////////////////////
IDirect3DSurface8* CDirect3D::GetTitleScreenOptions()
{
    return m_pTitleScreenOptions;
}
//////////////////////////////////////////////////////
// GetTitleScreen()
//////////////////////////////////////////////////////
IDirect3DSurface8* CDirect3D::GetTitleScreenQuit()
{
    return m_pTitleScreenQuit;
}